//
// Created by jack on 2021/2/5.
//
#include "weilapb_defines.h"

#include <WL.Service.pb.h>
#include <base64.h>
#include <pb_encode.h>
#include <stdio.h>

#include "weilapb_datafuns.h"

#include "weilapb_cryptutil.h"
#include "weilapb_error.h"
#include "weilapb_internal.h"
#include "weilapb_sessionmsg.h"

// Session Request
PBEncResult weilapb_buildNormalSessionMsgReq(PB_UINT32 commandId, WL_Session_SessionMessage* pParams) {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage = weilapb_buildServiceMsg(
        WL_Service_ServiceID_SERVICE_SESSION, commandId, WL_Service_CommandType_COMMAND_REQUEST);

    memset(&pbResult, 0, sizeof(PBEncResult));
    pbResult.message = weilapb_getMessageByCommandId(WL_Service_ServiceID_SERVICE_SESSION, commandId);
    if (pServiceMessage) {
        pServiceMessage->has_sessionMessage = true;
        memcpy(&pServiceMessage->sessionMessage, pParams, sizeof(WL_Session_SessionMessage));
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        } else {
            PB_FREE(pServiceMessage);
            pbResult.errCode = ERR_ENCODE;
        }
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildGetSessionReq(PB_UINT64 latestUpdateTimeStamp) {
    WL_Session_SessionMessage sessionMessage       = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqGetSession               = true;
    sessionMessage.reqGetSession.has_latestUpdated = true;
    sessionMessage.reqGetSession.latestUpdated     = latestUpdateTimeStamp;

    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_GET_SESSION, &sessionMessage);
}

PBEncResult weilapb_buildRemoveSessionReq(PB_UINT64 sessionId, PB_UINT16 sessionType) {
    WL_Session_SessionMessage sessionMessage        = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqRemoveSession             = true;
    sessionMessage.reqRemoveSession.has_sessionId   = true;
    sessionMessage.reqRemoveSession.sessionId       = sessionId;
    sessionMessage.reqRemoveSession.has_sessionType = true;
    sessionMessage.reqRemoveSession.sessionType     = sessionType;
    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_REMOVE_SESSION,
                                            &sessionMessage);
}

static PB_BOOL weilapb_encodeSessionInfo(pb_ostream_t* stream, const pb_field_t* field, const void* pData) {
    WL_Session_EmbeddedAttachSessionInfo sessionInfo = WL_Session_EmbeddedAttachSessionInfo_init_zero;
    PBSessionInfo*                       pInfo       = (PBSessionInfo*)pData;

    sessionInfo.has_sessionId   = true;
    sessionInfo.has_sessionType = true;
    sessionInfo.sessionId       = pInfo->sessionId;
    sessionInfo.sessionType     = pInfo->sessionType;

    if (pb_encode_submessage(stream, WL_Session_EmbeddedAttachSessionInfo_fields, &sessionInfo)) {
        return PB_TRUE;
    }

    return PB_FALSE;
}

PBEncResult weilapb_buildEmbeddedAttachSession(PBSessionInfo* pSessionInfoList, PB_UINT32 count) {
    PBFieldData               sessionInfos;
    WL_Session_SessionMessage sessionMessage    = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqEmbeddedAttachSession = true;

    weilapb_initEncDataItem(&sessionInfos, PB_DATA_TYPE_ARRAY_SUBMSG, count, pSessionInfoList);
    weilapb_setEncCallback(&sessionInfos, &sessionMessage.reqEmbeddedAttachSession.sessionInfos, PB_NULL);
    sessionInfos.encSubMsg  = weilapb_encodeSessionInfo;
    sessionInfos.subMsgSize = sizeof(PBSessionInfo);

    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_EMBEDDED_COMMAND_ATTACH_SESSION,
                                            &sessionMessage);
}

PBEncResult weilapb_buildBurstControlReq(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT16 burstType) {
    WL_Session_SessionMessage sessionMessage       = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqBurstControl             = true;
    sessionMessage.reqBurstControl.has_sessionId   = true;
    sessionMessage.reqBurstControl.sessionId       = sessionId;
    sessionMessage.reqBurstControl.has_sessionType = true;
    sessionMessage.reqBurstControl.sessionType     = sessionType;
    sessionMessage.reqBurstControl.has_burstType   = true;
    sessionMessage.reqBurstControl.burstType       = burstType;
    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_BURST_CONTROL, &sessionMessage);
}

PBEncResult weilapb_buildSetReadMsgReq(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT32 msgId) {
    WL_Session_SessionMessage sessionMessage  = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqMsgRead             = true;
    sessionMessage.reqMsgRead.has_sessionId   = true;
    sessionMessage.reqMsgRead.sessionId       = sessionId;
    sessionMessage.reqMsgRead.has_sessionType = true;
    sessionMessage.reqMsgRead.sessionType     = sessionType;
    sessionMessage.reqMsgRead.has_readMsgId   = true;
    sessionMessage.reqMsgRead.readMsgId       = msgId;
    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_READ_MSG, &sessionMessage);
}

PBEncResult
    weilapb_buildGetUnreadMsgReq(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT32 fromMsgId, PB_UINT32 toMsgId) {
    WL_Session_SessionMessage sessionMessage       = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqGetUnreadMsg             = true;
    sessionMessage.reqGetUnreadMsg.has_sessionId   = true;
    sessionMessage.reqGetUnreadMsg.sessionId       = sessionId;
    sessionMessage.reqGetUnreadMsg.has_sessionType = true;
    sessionMessage.reqGetUnreadMsg.sessionType     = sessionType;
    sessionMessage.reqGetUnreadMsg.has_fromMsgId   = true;
    sessionMessage.reqGetUnreadMsg.has_toMsgId     = true;
    sessionMessage.reqGetUnreadMsg.fromMsgId       = fromMsgId;
    sessionMessage.reqGetUnreadMsg.toMsgId         = toMsgId;
    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_GET_UNREAD_MSG,
                                            &sessionMessage);
}

PBEncResult weilapb_buildGetMsgReq(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT32 msgId, PB_UINT32 count) {
    WL_Session_SessionMessage sessionMessage = WL_Session_SessionMessage_init_zero;
    sessionMessage.has_reqGetMsg             = true;
    sessionMessage.reqGetMsg.has_sessionId   = true;
    sessionMessage.reqGetMsg.sessionId       = sessionId;
    sessionMessage.reqGetMsg.has_sessionType = true;
    sessionMessage.reqGetMsg.sessionType     = sessionType;
    sessionMessage.reqGetMsg.has_msgId       = true;
    sessionMessage.reqGetMsg.has_count       = true;
    sessionMessage.reqGetMsg.msgId           = msgId;
    sessionMessage.reqGetMsg.count           = count;
    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_GET_MSG, &sessionMessage);
}

PB_BOOL weilapb_encodeFileInfo(PBFileInfo* pFileInfo, PB_CHAR** pOut, PB_UINT32* pOutputSize, PB_CHAR* pServiceType) {
    PB_BOOL   ret         = true;
    PB_CHAR*  pFileFormat = NULL;
    PB_CHAR   fileExt[8]  = {0};
    PB_UINT32 fileNameBase64Len;
    PB_CHAR*  pFileNameBase64;

    PB_UINT32 fileNameLen = strlen(pFileInfo->fileName);
    PB_UINT32 count       = (fileNameLen > 1) ? fileNameLen - 1 : 0;
    PB_UINT32 totalLen =
        strlen(pFileInfo->fileUrl) + strlen(TEXT_EXTEND_START) + strlen(TEXT_EXTEND_END) + MAX_URL_STRING_LEN;

    if (fileNameLen) {
        while (pFileInfo->fileName[count--] != '.')
            ;
        if (count > 0) {
            memcpy(fileExt, &pFileInfo->fileName[count + 1], fileNameLen - count);
        }

        if (strstr(fileExt, ".txt")) {
            pFileFormat = FILE_TYPE_TXT;
        } else if (strstr(fileExt, ".doc") || strstr(fileExt, ".docx")) {
            pFileFormat = FILE_TYPE_DOC;
        } else if (strstr(fileExt, ".ppt")) {
            pFileFormat = FILE_TYPE_PPT;
        } else if (strstr(fileExt, ".pdf")) {
            pFileFormat = FILE_TYPE_PDF;
        } else if (strstr(fileExt, ".xls")) {
            pFileFormat = FILE_TYPE_XLS;
        } else if (strstr(fileExt, ".mp3")) {
            pFileFormat = FILE_TYPE_MP3;
        } else if (strstr(fileExt, ".wav")) {
            pFileFormat = FILE_TYPE_WAV;
        } else if (strstr(fileExt, ".html")) {
            pFileFormat = FILE_TYPE_HTML;
        } else if (strstr(fileExt, ".rar")) {
            pFileFormat = FILE_TYPE_RAR;
        } else if (strstr(fileExt, ".zip")) {
            pFileFormat = FILE_TYPE_ZIP;
        } else if (strstr(fileExt, ".raw")) {
            pFileFormat = FILE_TYPE_RAW;
        } else {
            pFileFormat = FILE_TYPE_UNKOWN;
        }

        if (strcmp(SERVICE_TYPE_VIDEO, pServiceType) == 0) {
            pFileFormat = FILE_TYPE_VIDEO;
        }

        fileNameBase64Len = wl_base64_encode_output_size(strlen(pFileInfo->fileName)) + 10;
        pFileNameBase64   = (PB_CHAR*)PB_ALLOC(fileNameBase64Len);
        if (pFileNameBase64 == NULL) {
            weilapb_error(ERR_OUT_OF_MEMORY, "allocate filename fail");
            return PB_FALSE;
        }

        wl_base64_encode(pFileInfo->fileName, fileNameLen, pFileNameBase64, &fileNameBase64Len);
        totalLen += fileNameBase64Len;
        *pOut = (PB_CHAR*)PB_ALLOC(totalLen);
        if (*pOut == NULL) {
            PB_FREE(pFileNameBase64);
            weilapb_error(ERR_OUT_OF_MEMORY, "allocate pOut fail");
            return PB_FALSE;
        }
        sprintf((char*)(*pOut),
                "%s%s?%s=%s&%s=%s&%s=%u&%s=%s%s",
                TEXT_EXTEND_START,
                pFileFormat,
                SERVICE_TYPE_KEY,
                pServiceType,
                FILE_URL_KEY,
                pFileInfo->fileUrl,
                FILE_SIZE_KEY,
                pFileInfo->fileSize,
                FILE_NAME_KEY,
                pFileNameBase64,
                TEXT_EXTEND_END);

        PB_FREE(pFileNameBase64);
    } else {
        pFileFormat = FILE_TYPE_UNKOWN;
        *pOut       = (PB_CHAR*)PB_ALLOC(totalLen);
        if (*pOut == NULL) {
            weilapb_error(ERR_OUT_OF_MEMORY, "allocate pOut fail");
            return PB_FALSE;
        }
        sprintf((char*)(*pOut),
                "%s%s?%s=%s&%s=%s&%s=%u%s",
                TEXT_EXTEND_START,
                pFileFormat,
                SERVICE_TYPE_KEY,
                pServiceType,
                FILE_URL_KEY,
                pFileInfo->fileUrl,
                FILE_SIZE_KEY,
                pFileInfo->fileSize,
                TEXT_EXTEND_END);
    }

    *pOutputSize = strlen((const PB_CHAR*)(*pOut));

    return ret;
}

PB_BOOL weilapb_cryptExtMsg(void* pExtMsg, PB_UINT16 msgType, PB_CHAR** outText, PB_UINT32* pOutputSize) {
    PB_BOOL   ret   = PB_FALSE;
    PB_CHAR*  pTemp = PB_NULL;
    PB_UINT32 outputSize;

    switch (msgType) {
        case IM_TEXT_MSG_TYPE: {
            pTemp = (PB_CHAR*)pExtMsg;
            ret = PB_TRUE;
        } break;

        case IM_FILE_INFO_TYPE: {
            ret = weilapb_encodeFileInfo((PBFileInfo*)pExtMsg, &pTemp, &outputSize, SERVICE_TYPE_FILE);
        } break;

        case IM_VIDEO_INFO_TYPE: {
            ret = weilapb_encodeFileInfo((PBFileInfo*)pExtMsg, &pTemp, &outputSize, SERVICE_TYPE_VIDEO);
        } break;

        case IM_LOCATION_INFO_TYPE: {
            PBLocationInfoShared* pbLocationInfoShared = (PBLocationInfoShared*)pExtMsg;
            pTemp                                      = (PB_CHAR*)PB_ALLOC(MAX_NORMAL_STRING_LEN);
            if (pTemp) {
                sprintf((PB_CHAR*)pTemp,
                        "%s%s?%s=%s&%s=%s&%s=%f&%s=%f&%s=%s&%s=%s%s",
                        TEXT_EXTEND_START,
                        pbLocationInfoShared->locationMapUrl,
                        SERVICE_TYPE_KEY,
                        LOCATION_TYPE_KEY,
                        LOCATION_TYPE_KEY,
                        pbLocationInfoShared->locationType,
                        LATITUDE_KEY,
                        pbLocationInfoShared->latitude,
                        LONGITUDE_KEY,
                        pbLocationInfoShared->longitude,
                        LOCATION_NAME_KEY,
                        pbLocationInfoShared->locationName,
                        LOCATION_ADDRESS_KEY,
                        pbLocationInfoShared->locationAddr,
                        TEXT_EXTEND_END);
                outputSize = strlen(pTemp);
                ret        = PB_TRUE;
            }
        } break;

        case IM_COMMAND_TYPE: {
            ret = PB_TRUE;
        } break;

        case IM_IMAGE_INFO_TYPE: {
            pTemp = (PB_CHAR*)PB_ALLOC(MAX_NORMAL_STRING_LEN);
            if (pTemp) {
                sprintf((PB_CHAR*)pTemp, "%s%s%s", TEXT_EXTEND_START, (PB_CHAR*)pExtMsg, TEXT_EXTEND_END);
                outputSize = strlen(*outText);
                ret        = PB_TRUE;
            }
        } break;
    }

    if (ret == PB_TRUE) {
        if (weilapb_encryptText(pTemp, strlen(pTemp), outText, pOutputSize) == 0) {
            ret = PB_TRUE;
        }
    }

    if (msgType != IM_TEXT_MSG_TYPE) {
        if (pTemp) {
            PB_FREE(pTemp);
        }
    }

    return ret;
}

PBEncResult weilapb_buildExtendMsg(PB_UINT64 sessionId,
                                   PB_UINT16 sessionType,
                                   PB_UINT32 senderId,
                                   void*     pExtMsg,
                                   PB_UINT16 msgType) {
    PBEncResult                pbResult;
    PB_CHAR*                   cryptedText     = PB_NULL;
    PB_UINT32                  cryptedTextSize = 0;
    PBFieldData                textField;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_SESSION,
                                WL_Session_SessionCommandId_SESSION_COMMAND_MSGDATA,
                                WL_Service_CommandType_COMMAND_REQUEST);

    if (pServiceMessage) {
        pServiceMessage->has_sessionMessage                = true;
        pServiceMessage->sessionMessage.has_reqMsg         = true;
        pServiceMessage->sessionMessage.reqMsg.has_msgData = true;

        pServiceMessage->sessionMessage.reqMsg.msgData.has_sessionType = true;
        pServiceMessage->sessionMessage.reqMsg.msgData.sessionType     = sessionType;

        pServiceMessage->sessionMessage.reqMsg.msgData.has_sessionId = true;
        pServiceMessage->sessionMessage.reqMsg.msgData.sessionId     = sessionId;

        pServiceMessage->sessionMessage.reqMsg.msgData.has_msgId = true;
        pServiceMessage->sessionMessage.reqMsg.msgData.msgId = 0;

        pServiceMessage->sessionMessage.reqMsg.msgData.has_senderId = true;
        pServiceMessage->sessionMessage.reqMsg.msgData.senderId     = senderId;

        pServiceMessage->sessionMessage.reqMsg.msgData.has_msgType = true;
        pServiceMessage->sessionMessage.reqMsg.msgData.msgType = WL_Session_MessageType_MSG_TYPE_TEXT;

        pServiceMessage->sessionMessage.reqMsg.msgData.has_created = true;
        pServiceMessage->sessionMessage.reqMsg.msgData.created = 0;

        if (weilapb_cryptExtMsg(pExtMsg, msgType, &cryptedText, &cryptedTextSize)) {
            weilapb_initEncDataItem(&textField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(cryptedText), cryptedText);
            weilapb_setEncCallback(&textField, &pServiceMessage->sessionMessage.reqMsg.msgData.textData, PB_NULL);

            if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
                PB_FREE(cryptedText);
                PB_FREE(pServiceMessage);
                return pbResult;
            }
        } else {
            pbResult.errCode = ERR_ENCODE;
        }
        PB_FREE(pServiceMessage);
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildSendText(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT32 senderId, PB_CHAR* pText) {
    return weilapb_buildExtendMsg(sessionId, sessionType, senderId, pText, IM_TEXT_MSG_TYPE);
}

PBEncResult weilapb_buildSendImage(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT32 senderId, PB_CHAR* pImageUrl) {
    return weilapb_buildExtendMsg(sessionId, sessionType, senderId, pImageUrl, IM_IMAGE_INFO_TYPE);
}

PBEncResult weilapb_buildSendFile(PB_UINT64   sessionId,
                                  PB_UINT16   sessionType,
                                  PB_UINT32   senderId,
                                  PBFileInfo* pFileInfo,
                                  PB_BOOL     isVideo) {
    if (isVideo) {
        return weilapb_buildExtendMsg(sessionId, sessionType, senderId, pFileInfo, IM_VIDEO_INFO_TYPE);
    } else {
        return weilapb_buildExtendMsg(sessionId, sessionType, senderId, pFileInfo, IM_FILE_INFO_TYPE);
    }
}

PBEncResult weilapb_buildSendLocationShare(PB_UINT64             sessionId,
                                           PB_UINT16             sessionType,
                                           PB_UINT32             senderId,
                                           PBLocationInfoShared* pSharedInfo) {
    return weilapb_buildExtendMsg(sessionId, sessionType, senderId, pSharedInfo, IM_LOCATION_INFO_TYPE);
}

PB_BOOL weilapb_encodeOpusData(PBLinkList* pFrameList, PB_UINT8** pEncodedData, PB_UINT32* pEncodedDataSize) {
    PB_UINT32 totalSize = 0;
    PB_UINT8* pTarget;
    PB_UINT32 offset = 0;

    PBLinkItem* pItem = pFrameList->pLinks;
    int         count = 0;
    while (pItem) {
        totalSize += pItem->dataSize;
        count++;
        pItem = pItem->pNext;
    }

    *pEncodedData = (PB_UINT8*)PB_ALLOC(totalSize + pFrameList->listCount * 4);
    pTarget       = *pEncodedData;
    if (pTarget) {
        pItem = pFrameList->pLinks;
        while (pItem) {
            pTarget[offset++] = pItem->data[0];
            if (pItem->data[0] & 0x80) {
                pTarget[offset++] = (PB_UINT8)(((PB_UINT16)(pItem->dataSize - 1) & 0xFF00) >> 8);
                pTarget[offset++] = (PB_UINT8)((PB_UINT16)(pItem->dataSize - 1) & 0xFF);
            } else {
                pTarget[offset++] = (PB_UINT8)((PB_UINT16)(pItem->dataSize - 1) & 0xFF);
            }

            memcpy(&pTarget[offset], &(pItem->data[1]), pItem->dataSize - 1);
            offset += (pItem->dataSize - 1);
            pItem = pItem->pNext;
        }
        *pEncodedDataSize = offset;
        //        printf("offset:%d allocaSize:%d\n", offset, totalSize + pFrameList->listCount * 4);
        return PB_TRUE;
    }

    return PB_FALSE;
}

PBEncResult weilapb_buildSendBurstData(PBBurstData* burstData) {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_SESSION,
                                WL_Session_SessionCommandId_SESSION_COMMAND_MSGDATA,
                                WL_Service_CommandType_COMMAND_REQUEST);

    memset(&pbResult, 0, sizeof(PBEncResult));
    pbResult.message = weilapb_getMessageByCommandId(WL_Service_ServiceID_SERVICE_SESSION,
                                                     WL_Session_SessionCommandId_SESSION_COMMAND_MSGDATA);
    if (pWeilaCtx) {
        if (pServiceMessage) {
            PB_UINT8*   pEncodedOpus    = PB_NULL;
            PB_UINT32   encodedOpusSize = 0;
            PBFieldData pttDataField;

            pServiceMessage->has_sessionMessage                = true;
            pServiceMessage->sessionMessage.has_reqMsg         = true;
            pServiceMessage->sessionMessage.reqMsg.has_msgData = true;

            pServiceMessage->sessionMessage.reqMsg.msgData.has_sessionType = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.sessionType     = burstData->sessionType;

            pServiceMessage->sessionMessage.reqMsg.msgData.has_msgType = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.msgType     = WL_Session_MessageType_MSG_TYPE_PTT;
            if (burstData->isVoip) {
                pServiceMessage->sessionMessage.reqMsg.msgData.msgType = WL_Session_MessageType_MSG_TYPE_VOIP;
            }

            pServiceMessage->sessionMessage.reqMsg.msgData.has_sessionId = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.sessionId     = burstData->sessionId;

            pServiceMessage->sessionMessage.reqMsg.msgData.has_senderId = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.senderId     = burstData->senderId;

            pServiceMessage->sessionMessage.reqMsg.msgData.has_pttData        = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.has_source = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.source     = burstData->sourceType;

            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.has_marker = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.marker     = burstData->marker;

            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.has_packageSeq = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.packageSeq     = burstData->packetSeq;

            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.has_seq = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.seq     = burstData->burstSeq;

            if (burstData->isMonitor) {
                pServiceMessage->sessionMessage.reqMsg.msgData.pttData.has_monitor = true;
                pServiceMessage->sessionMessage.reqMsg.msgData.pttData.monitor     = burstData->isMonitor;
            }

            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.has_opusData            = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.opusData.has_frameCount = true;
            pServiceMessage->sessionMessage.reqMsg.msgData.pttData.opusData.frameCount     = 0;
            if (burstData->pFrameList) {
                pServiceMessage->sessionMessage.reqMsg.msgData.pttData.opusData.frameCount =
                    burstData->pFrameList->listCount;
                if (burstData->pFrameList->listCount) {
                    if (weilapb_encodeOpusData(burstData->pFrameList, &pEncodedOpus, &encodedOpusSize)) {
                        weilapb_initEncDataItem(
                            &pttDataField, PB_DATA_TYPE_FIXED_SIZE_STRING, encodedOpusSize, pEncodedOpus);
                        weilapb_setEncCallback(&pttDataField,
                                               &pServiceMessage->sessionMessage.reqMsg.msgData.pttData.opusData.payload,
                                               PB_NULL);
                    } else {
                        pbResult.errCode = ERR_OUT_OF_MEMORY;
                        PB_FREE(pServiceMessage);
                        return pbResult;
                    }
                }
            }
            if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
                if (pEncodedOpus) {
                    PB_FREE(pEncodedOpus);
                }
                PB_FREE(pServiceMessage);
                return pbResult;
            } else {
                PB_FREE(pServiceMessage);
                pbResult.errCode = ERR_ENCODE;
            }
        } else {
            pbResult.errCode = ERR_OUT_OF_MEMORY;
        }
    }

    return pbResult;
}

void weilapb_startPttAudio(PB_UINT64 sessionId, PB_UINT16 sessionType, PB_UINT32 senderId) {
    if (pWeilaCtx) {
        const PB_UINT32 maxInt = (uint32_t)(0xFFFFFFFF - 4);
        pWeilaCtx->curPttSeq++;
        if (pWeilaCtx->curPttSeq > maxInt) {
            pWeilaCtx->curPttSeq = 0;
        }
        pWeilaCtx->curPttMarker      = FIRST_PTT_FRAME;
        pWeilaCtx->curPttSenderId    = senderId;
        pWeilaCtx->curPttSessionId   = sessionId;
        pWeilaCtx->curPttSessionType = sessionType;
    }
}

PBEncResult weilapb_buildSendPttFrames(PB_UINT32 frameType, PB_UINT32 sourceType, PBLinkList* pFrameList) {
    PBBurstData burstData;
    memset(&burstData, 0, sizeof(PBBurstData));
    burstData.sessionId   = pWeilaCtx->curPttSessionId;
    burstData.sessionType = pWeilaCtx->curPttSessionType;
    burstData.senderId    = pWeilaCtx->curPttSenderId;
    burstData.sourceType  = sourceType;
    burstData.burstSeq    = pWeilaCtx->curPttSeq;
    burstData.packetSeq   = pWeilaCtx->curPttPackageSeq;
    burstData.isMonitor   = false;
    burstData.marker      = frameType;
    burstData.pFrameList  = pFrameList;

    return weilapb_buildSendBurstData(&burstData);
}

PBEncResult weilapb_buildVoipInviteReq(PB_CHAR* callId, PB_UINT32 userId) {
    WL_Session_SessionMessage message = WL_Session_SessionMessage_init_zero;
    PBFieldData               callIdField;

    message.has_reqVoipInvite = true;
    weilapb_initEncDataItem(&callIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(callId), callId);
    weilapb_setEncCallback(&callIdField, &message.reqVoipInvite.callId, PB_NULL);
    message.reqVoipInvite.has_userId = true;
    message.reqVoipInvite.userId     = userId;

    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_VOIP_INVITE, &message);
}

PBEncResult weilapb_buildVoipAcceptReq(PB_CHAR* callId, PB_UINT32 userId) {
    WL_Session_SessionMessage message = WL_Session_SessionMessage_init_zero;
    PBFieldData               callIdField;

    message.has_reqVoipAccept = true;
    weilapb_initEncDataItem(&callIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(callId), callId);
    weilapb_setEncCallback(&callIdField, &message.reqVoipAccept.callId, PB_NULL);
    message.reqVoipAccept.has_userId = true;
    message.reqVoipAccept.userId     = userId;

    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_VOIP_ACCEPT, &message);
}

PBEncResult weilapb_buildVoipHangupReq(PB_CHAR* callId, PB_UINT32 userId) {
    WL_Session_SessionMessage message = WL_Session_SessionMessage_init_zero;
    PBFieldData               callIdField;

    message.has_reqVoipHangup = true;
    weilapb_initEncDataItem(&callIdField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(callId), callId);
    weilapb_setEncCallback(&callIdField, &message.reqVoipHangup.callId, PB_NULL);
    message.reqVoipHangup.has_userId = true;
    message.reqVoipHangup.userId     = userId;

    return weilapb_buildNormalSessionMsgReq(WL_Session_SessionCommandId_SESSION_COMMAND_VOIP_HANGUP, &message);
}
